﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Globalization;
using System.Collections;
using System.Reflection;

namespace TR0217.ControlEx
{
    public class TriPaddingConverter : TypeConverter
    {

        public TriPaddingConverter()
        {        }

        /// <summary>
        /// Determines whether this converter can convert an object in the specified
        /// source type to the native type of the converter.
        /// </summary>
        /// <param name="context">
        /// A System.ComponentModel.ITypeDescriptorContext that can be used to get additional
        /// information about the environment this converter is being called from. This
        /// may be null, so you should always check. Also, properties on the context
        /// object may also return null.
        ///</param>
        /// <param name="sourceType">The type you want to convert from.</param>
        /// <returns>This method returns true if this object can perform the conversion; otherwise,
        /// false.
        /// </returns>
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;
            return base.CanConvertFrom(context, sourceType);
        }
        /// <summary>
        /// Gets a value indicating whether this converter can convert an object to the
        /// given destination type using the context.
        /// </summary>
        /// <param name="context">
        /// A System.ComponentModel.ITypeDescriptorContext that can be used to get additional
        /// information about the environment this converter is being called from. This
        /// can be null, so always check. Also, properties on the context object can
        /// return null.
        /// </param>
        /// <param name="destinationType">A System.Type that represents the type you want to convert to.</param>
        /// <returns>This method returns true if this converter can perform the conversion; otherwise,
        /// false.
        /// </returns>
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor))
                return true;
            return base.CanConvertTo(context, destinationType);
        }
        /// <summary>
        /// Converts the specified object to the converter's native type.
        /// </summary>
        /// <param name="context">
        /// A System.ComponentModel.ITypeDescriptorContext that can be used to get additional
        /// information about the environment this converter is being called from. This
        /// may be null, so you should always check. Also, properties on the context
        /// object may also return null.
        /// </param>
        /// <param name="culture">
        /// An System.Globalization.CultureInfo object that contains culture specific
        /// information, such as the language, calendar, and cultural conventions associated
        /// with a specific culture. It is based on the RFC 1766 standard.
        /// </param>
        /// <param name="value">The object to convert.</param>
        /// <returns>The converted object.</returns>
        /// <exception cref="System.NotSupportedException:">The conversion cannot be completed.</exception>
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value is string)
            {
                if (culture == null)
                    culture = CultureInfo.InvariantCulture;
                string sValue = (string)value;
                if (sValue == string.Empty)
                    sValue = "0, 0, 0,100";
                string[] numbers = sValue.Split(new char[] { ',' });
                object[] values = null;
                Type[] types = null;

                if (numbers.Length == 4)
                {
                    values = new object[numbers.Length];
                    types = new Type[numbers.Length];
                    for (int i = 0; i < numbers.Length; i++)
                    {
                        values[i] = int.Parse(numbers[i], culture.NumberFormat);
                        types[i] = typeof(int);
                    }
                }
                if (values != null)
                {
                    Type type = GetTriPaddingType(context);
                    ConstructorInfo constructorInfo = type.GetConstructor(types);
                    return constructorInfo.Invoke(values);
                }
            }
            return base.ConvertFrom(context, culture, value);
        }
        /// <summary>
        /// Converts the specified object to the specified type.
        /// </summary>
        /// <param name="context">
        /// A System.ComponentModel.ITypeDescriptorContext that can be used to get additional
        /// information about the environment this converter is being called from. This
        /// may be null, so you should always check. Also, properties on the context
        /// object may also return null.
        /// </param>
        /// <param name="culture">
        /// An System.Globalization.CultureInfo object that contains culture specific
        /// information, such as the language, calendar, and cultural conventions associated
        /// with a specific culture. It is based on the RFC 1766 standard.
        /// </param>
        /// <param name="value">The object to convert.</param>
        /// <param name="destinationType">The type to convert the object to.</param>
        /// <returns>The converted object.</returns>
        /// <exception cref="System.NotSupportedException:">The conversion cannot be completed.</exception>
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            Type type = value.GetType();
            int top = (int)type.GetProperty("Top").GetValue(value, null);
            int bottom = (int)type.GetProperty("Bottom").GetValue(value, null);
            int side = (int)type.GetProperty("Side").GetValue(value, null);
            int width = (int)type.GetProperty("Width").GetValue(value, null);
            if (destinationType == typeof(string))
            {
                if (culture == null)
                    culture = CultureInfo.InvariantCulture;
                return string.Format("({0},{1},{2},{3})", top, bottom, side, width);
            }
            else if (destinationType == typeof(InstanceDescriptor))
            {
                ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) });
                if (constructorInfo != null)
                    return new InstanceDescriptor(constructorInfo, new object[] { top, bottom, side, width });
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
        /// <summary>
        /// Creates an object of this type by using a specified set of property values
        /// for the object. This is useful for creating non-changeable objects that have
        /// changeable properties.
        /// </summary>
        /// <param name="context">
        /// A System.ComponentModel.TypeDescriptor through which additional context can
        /// be provided.
        /// </param>
        /// <param name="propertyValues">
        /// A dictionary of new property values. The dictionary contains a series of
        /// name-value pairs, one for each property returned from the 
        /// Size3DConverter.GetProperties(System.ComponentModel.ITypeDescriptorContext,System.Object,System.Attribute[])
        /// method.
        /// </param>
        /// <returns>
        /// The newly created object, or null if the object could not be created. The
        /// default implementation returns null.
        /// </returns>
        public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
        {
            Type type = GetTriPaddingType(context);
            ConstructorInfo constructorInfo = type.GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) });
            return constructorInfo.Invoke(new object[] {
                propertyValues["Top"],
                propertyValues["Bottom"],
                propertyValues["Side"],
                propertyValues["Width"]});
        }
        /// <summary>
        /// Determines whether changing a value on this object should require a call to the
        /// Size3DConverter.CreateInstance(System.ComponentModel.ITypeDescriptorContext,System.Collections.IDictionary)
        /// method to create a new value.
        /// </summary>
        /// <param name="context">
        /// A System.ComponentModel.TypeDescriptor through which additional context can
        /// be provided.
        /// </param>
        /// <returns>
        /// true if the System.Drawing.SizeConverter.CreateInstance(System.ComponentModel.ITypeDescriptorContext,System.Collections.IDictionary)
        /// object should be called when a change is made to one or more properties of
        /// this object.
        /// </returns>
        public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        /// <summary>
        /// Retrieves the set of properties for this type. By default, a type does not
        /// have any properties to return.
        /// </summary>
        /// <param name="context">
        /// A System.ComponentModel.TypeDescriptor through which additional context can
        /// be provided.
        /// </param>
        /// <param name="value">The value of the object to get the properties for.</param>
        /// <param name="attributes">An array of System.Attribute objects that describe the properties.</param>
        /// <returns>
        /// The set of properties that should be exposed for this data type. If no properties
        /// should be exposed, this may return null. The default implementation always
        /// returns null.
        /// </returns>
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            Type type = GetTriPaddingType(context);
            return TypeDescriptor.GetProperties(type, attributes).Sort(new string[] {"Top","Bottom","Side", "Width" });
        }
        /// <summary>
        /// Determines whether this object supports properties. By default, this is false.
        /// </summary>
        /// <param name="context">
        /// A System.ComponentModel.TypeDescriptor through which additional context can
        /// be provided.
        /// </param>
        /// <returns>
        /// true if the Size3DConverter.GetProperties(System.ComponentModel.ITypeDescriptorContext,System.Object,System.Attribute[])
        /// method should be called to find the properties of this object.
        /// </returns>
        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        private Type GetTriPaddingType(ITypeDescriptorContext context)
        {
            if (context == null)
                return typeof(TriPadding);
            return context.PropertyDescriptor.PropertyType;
        }
    }
}
